/**
 * 维护歌单 当前歌曲播放进度
 */
const service = require('./');
const Utils = require('../utils');
const DAO = require('./dao');
const MUSIC_LIST = [];
const CHANGE_SONG = new Set();

class MusicPlat {

  constructor() {
    this.init();
  }

  async init() {
    try {
      if (!MUSIC_LIST.length) {
        const list = await this.songList();
        MUSIC_LIST.push(...list);
        MUSIC_LIST.length &&
          this.initSong();
      }
    } catch (error) {
      Utils.Logger.error(error.stack);
    }
  }

  async initSong() {
    if (!MUSIC_LIST.length) return;
    const song = MUSIC_LIST[0].song;
    let update = false;
    if (song.lyric === undefined) {
      song.lyric = await service.music.getSongLyric(song.id);
      update = true;
    }
    if (
      !song.url ||
      !song.url_dead ||
      new Date(song.url_dead).getTime() <= new Date().getTime()
    ) {
      const detail = await service.music.getSong(song.id);
      const now = new Date();
      now.setSeconds(now.getSeconds() + detail.expi);
      song.url = detail.url;
      song.url_dead = now;
      update = true;
    }
    song.current = 0;
    this.start();
    service.websocket.changeSong();
    update && this.updateSong(song);
  }

  start() {
    if (this.timer) return;
    this.timer = setInterval(() => {
      const song = MUSIC_LIST[0];
      if (song) {
        if (song.song.current < song.song.duration) {
          song.song.current += 1;
          return;
        }
      }
      this.change();
    }, 1000);
  }

  change() {
    const song = MUSIC_LIST.shift();
    song && this.played(song);
    CHANGE_SONG.clear();
    if (MUSIC_LIST.length) {
      this.initSong();
      return;
    }
    this.init();
  }

  async played(song) {
    try {
      await DAO.updateOne('Musics', {
        where: {
          songId: song.song.id
        },
        updateFields: {
          updateTime: new Date()
        },
        options: {
          $inc: {
            playCount: 1
          }
        }
      });
    } catch (error) {
      Utils.Logger.error(error.stack);
    }
  }

  async updateSong(song) {
    try {
      await DAO.updateOne('Musics', {
        where: {
          songId: song.id
        },
        updateFields: {
          updateTime: new Date(),
          song
        },
      });
    } catch (error) {
      Utils.Logger.error(error.stack);
    }
  }

  async songList() {
    return await DAO.find('Musics', {
      where: {
        $or: [{
          playCount: 0,
        }, {
          repeat: true
        }]
      },
      fields: null,
      options: {
        sort: {
          'createTime': 'desc'
        },
        limit: 8
      }
    });
  }

  async addSong(song, request, options) {
    const _song = {
      song,
      songId: song.id,
      adder: Utils.Func.getIP(request),
      playCount: 0,
      repeat: false,
      createTime: new Date(),
      updateTime: new Date(),
      ...options
    };
    const exist = await DAO.findOne('Musics', {
      where: {
        songId: song.id
      },
      fields: {
        _id: 1
      }
    });
    if (!exist) {
      await DAO.createMany('Musics', [_song]);
    } else {
      await DAO.updateOne('Musics', {
        where: {
          songId: song.id
        },
        updateFields: {
          updateTime: new Date(),
          ...options
        },
      });
    }
    MUSIC_LIST.length < 8 &&
      MUSIC_LIST.push(_song);
    service.websocket.userAddSong({
      from: _song.adder,
      name: song.name
    });
  }

  getSongs() {
    return MUSIC_LIST.slice(1).map(l => {
      return {
        ...l.song,
        adder: l.adder ? Utils.Func.encodeIp(l.adder) : 'System'
      };
    });
  }

  getFirstSong() {
    const first = MUSIC_LIST[0];
    first.song.current = first.song.current || 0;
    return first;
  }

  next(ID) {
    const cur = CHANGE_SONG.size;
    CHANGE_SONG.add(ID);
    const _cur = CHANGE_SONG.size;
    if (cur === _cur) return false;
    const all = service.websocket.len;
    if (!all || _cur / all > 0.3) {
      this.change();
    }
    return true;
  }

}


module.exports = new MusicPlat();